Skip to content

Add dense-array-base representation handle to ProbeGroup#425

Open
h-mayorquin wants to merge 5 commits intoSpikeInterface:mainfrom
h-mayorquin:dense_array_handle
Open

Add dense-array-base representation handle to ProbeGroup#425
h-mayorquin wants to merge 5 commits intoSpikeInterface:mainfrom
h-mayorquin:dense_array_handle

Conversation

@h-mayorquin
Copy link
Copy Markdown
Collaborator

@h-mayorquin h-mayorquin commented Apr 19, 2026

Coming here from #420

This PR adds a new private _contact_vector property on ProbeGroup. The property is a dense channel-ordered read-only array derived from the current device_channel_indices:

>>> probegroup._contact_vector
array([(0,  0.,  0., '0', 'front'),
       (0, 20.,  0., '0', 'front'),
       (0,  0., 20., '1', 'back'),
       (0, 20., 20., '1', 'back')],
      dtype=[('probe_index', '<i8'), ('x', '<f8'), ('y', '<f8'), ('shank_ids', '<U64'), ('contact_sides', '<U8')])

It contains only the subset of state SpikeInterface actually needs in channel order: probe_index, x, y, z, shank_ids, and contact_sides. Fields that are not available on the ProbeGroup are not included in the representation. The property is private by convention: it is intended for integration with downstream libraries (notably SpikeInterface), and its fields and dtype may evolve with consumer requirements, so user code should not depend on it directly.

This PR enables a middle way between the old contact_vector workflow we are currently using in SpikeInterface and the full array-backed rewrite of ProbeGroup proposed in #420. #420 changes the identity of ProbeGroup itself to become an array-backed backend; this PR keeps the scope smaller and adds a cached attribute with only what SpikeInterface needs. The coupling between probeinterface and SpikeInterface now lives in one small surface (_contact_vector and _build_contact_vector()) instead of being spread through the whole ProbeGroup class, so the two libraries can evolve together without requiring larger changes on either side. This provides the probeinterface-side handle needed for the SpikeInterface #4465 direction, where the recording holds a ProbeGroup object directly and recovers the channel-facing behavior through this handle on demand.

Three technical points:

  • SpikeInterface integration. The cache is invalidated on probegroup mutations (add_probe, set_global_device_channel_indices, auto_generate_*) and rebuilt lazily on first access, so consumers just read probegroup._contact_vector and do not have to track when to rebuild. Probe-level mutations (e.g. probe.move) do not invalidate the cache; this keeps ProbeGroup unaware of Probe internals and avoids the container/contained coupling that Refactor probegroup to be array-based #420 introduces via a _probe_group back-reference and partial mutation guards on Probe. The only case where a consumer must call _build_contact_vector() explicitly is after mutating a probe post-attachment which is a a rare, well-defined exception that should be handled at the consumer side.

  • Private to make it flexible. The property is private because its fields and dtype are tailored to SpikeInterface's current needs and will evolve with them. As a private attribute, the handle can grow, shrink, or change shape without becoming a public-API break. Keeping it private also avoids a footgun: someone mutating a probe after attaching the probegroup and expecting the changes to propagate to the cache (see the previous bullet). This mechanism is for SpikeInterface and for power users who know the contract, so let's keep it private and safe.

  • No serialization format change. The cache lives in memory only; it is rebuilt at access time from each probe's own device_channel_indices, which already round-trip through probe serialization. So this PR needs no format version bump and no new way to encode channel order in the JSON/dict representation. The existing per-probe serialization is sufficient. Because the handle derives from device_channel_indices rather than replacing it, a consumer could also preserve the user-provided wiring as provenance and still obtain a channel-ordered view.

The companion SpikeInterface PR illustrates the implementation of this.

@h-mayorquin h-mayorquin marked this pull request as ready for review April 20, 2026 05:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant